package com.tcp2.handler;

/**
 * Author: 杨俊辉
 * Time: 2014-09-22 17:35
 * Copyright (C) 2014 Xiamen Yaxon Networks CO.,LTD.
 */

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.BaseEncoding;
import com.tcp2.constant.Constants;
import com.tcp2.msg.VehicleInfo;
import com.tcp2.msg.rpc.*;
import com.tcp2.net.PlatformMap;
import com.tcp2.net.SlaveLink;
import com.tcp2.server.ProtoMsg;
import com.tcp2.util.DeferredResultUtil;
import com.tcp2.util.ProtoUtil;
import com.yaxon.vndp.common.util.CodecUtils;
import com.yaxon.vndp.dms.DmsContext;
import com.yaxon.vndp.dms.Message;
import com.yaxon.vndp.dms.MessageHandler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.tcp2.server.ProtoConstants.*;
/**
 * 车辆监管处理类
 */
@Service
public class VehicleMonitoringHandler extends BaseMsgHandler implements MessageHandler {

    protected static Logger logger = LoggerFactory.getLogger(VehicleMonitoringHandler.class);
    private static BaseEncoding hex = BaseEncoding.base16().withSeparator(" ", 2);
    private ObjectMapper objectMapper = new ObjectMapper();

    @Value("#{configProperties['dxs.tmpDir']}")
    private String tmpDir;

    @Value("#{configProperties['fss.url']}")
    private String fssUrl;
    @Override
    public void handle(ProtoMsg msg) {

        logger.info("-> 收到主链路， 车辆监管，消息id：0x{}，下级平台接入码：{}", CodecUtils.shortToHex((short) msg.msgId), msg.gnssCenterId);
        ByteBuf data = msg.msgData;
        String lpn = ProtoUtil.readOctetString(data, 21);
        byte color = data.readByte();
        int dataType = data.readUnsignedShort(); //数据类型
        int dataLen = data.readInt();//后续数据长度
        if (dataLen != data.readableBytes()) {
            logger.warn("(UP_CTRL_MSG):数据长度不一致");
            return;
        }

        switch (dataType) {
            case UP_CTRL_MSG_MONITOR_VEHICLE_ACK:
                doUpCtrlMsgMonitorVehicle(lpn, color, data, dataType);
                break;
            case UP_CTRL_MSG_TAKE_PHOTO_ACK:
                doUpCtrlMsgTakePhotoAck(lpn, color, data, dataType);
                break;
            case UP_CTRL_MSG_TEXT_INFO_ACK:
                doUpCtrlMsgTextInfo(lpn, color, data, dataType);
                break;
            case UP_CTRL_MSG_TAKE_TRAVEL_ACK:
                doUpCtrlMsgTakeTravelAck(lpn, color, data, dataType);
                break;
            case UP_CTRL_MSG_EMERGENCY_MONITORING_ACK:
                doUpCtrlMsgEmergenctMonitoringAck(lpn, color, data, dataType);
                break;

            default:
                throw new RuntimeException("未知子业务类型：0x" + CodecUtils.shortToHex((short) dataType));

        }
    }

    /**
     * RPC消息处理器
     */
    @Override
    public void onReceive(DmsContext ctx, Message req) {
        if (req instanceof Dn_9501) {
            doDownCtrlMsgMonitorVehicleReq(ctx, (Dn_9501) req);
        } else if (req instanceof Dn_9502) {
            doDownCtrlMsgTakePhotoReq(ctx, (Dn_9502) req);
        } else if (req instanceof Dn_9503) {
            doDownCtrlMsgTextInfo(ctx, (Dn_9503) req);
        } else if (req instanceof Dn_9504) {
            doDownCtrlMsgTakeTravelReq(ctx, (Dn_9504) req);
        } else if (req instanceof Dn_9505) {
            doUpCtrlMsgEmergencyMonitoringReq(ctx, (Dn_9505) req);
        }
        else {
            throw new RuntimeException("未知的请求消息类型: " + req.getClass().getName());
        }

    }

    private void doUpCtrlMsgMonitorVehicle(String lpn, byte color, ByteBuf data, int dataType) {
        logger.info("-> 收到主链路，车辆单向监听应答，子业务类型 ：0x{}", CodecUtils.shortToHex((short) dataType));
        logger.info("字节数{}，16进制:{}", data.readableBytes(), ByteBufUtil.hexDump(data).replaceAll("\\w{2}(?!$)", "$0 ").toUpperCase());
        Up_1501 m = new Up_1501();
        m.setLpn(lpn);
        m.setCol(color);
        m.setResult(data.readByte());
        String fno = "1501:" + lpn + ":" + color;
        DeferredResultUtil.getInstance().setResult(fno, m);

    }

    private void doUpCtrlMsgTakePhotoAck(String lpn, byte color, ByteBuf data, int dataType) {




    }

    /**
     * 解析图片字节流，上传图片到文件服务器，保存记录到数据库
     * @param vid
     * @param res
     */
    private boolean UpLoadPhoto(Long vid, Up_1502 res) {
        logger.info("拍照协议图片处理开始...");
        /**
         * 第一步：解析图片流，保存到临时文件夹
         */
        String photoType = "";
        if (res.getType() == Constants.PHOTO_TYPE_1) {
            photoType = ".jpg";
        }
        if (res.getType() == Constants.PHOTO_TYPE_2) {
            photoType = ".gif";
        }
        if (res.getType() == Constants.PHOTO_TYPE_3) {
            photoType = ".tiff";
        }
        if (res.getType() == Constants.PHOTO_TYPE_4) {
            photoType = ".png";
        }
        if (photoType.equals("")) {
            logger.warn("图片格式不正确,处理失败!");
            return false;
        }
        return true;
    }





    private void doUpCtrlMsgTextInfo(String lpn, byte color, ByteBuf data, int dataType) {
        logger.info("-> 收到主链路，下发车辆报文应答，子业务类型 ：0x{}", CodecUtils.shortToHex((short) dataType));
        logger.info("字节数{}，16进制:{}", data.readableBytes(), ByteBufUtil.hexDump(data).replaceAll("\\w{2}(?!$)", "$0 ").toUpperCase());
        Up_1503 m = new Up_1503();
        try {
            m.setLpn(lpn);
            m.setCol(color);
            m.setMsgId(data.readInt());
            m.setResult(data.readByte());
        } catch (Exception e) {
            m.setRc((byte) 0x00);
            logger.warn("收到主链路，下发车辆报文应答异常", e);
        }
        String fno = "1503:" + lpn + ":" + color;
        DeferredResultUtil.getInstance().setResult(fno, m);

    }

    private void doUpCtrlMsgTakeTravelAck(String lpn, byte color, ByteBuf data, int dataType) {
        logger.info("-> 收到主链路，上报车辆行驶记录应答，子业务类型 ：0x{}", CodecUtils.shortToHex((short) dataType));
        if (logger.isDebugEnabled()) {
            logger.info("字节数{}，16进制:{}", data.readableBytes(), ByteBufUtil.hexDump(data).replaceAll("\\w{2}(?!$)", "$0 ").toUpperCase());
        }
        final Up_1504 m = new Up_1504();
        try {
            m.setLpn(lpn);
            m.setCol(color);
            m.setCommandType(data.readByte());
            if (m.getCommandType() == (byte) 0x10) {
                String traveldataInfo = ProtoUtil.readU32String(data);
                byte[] bytes = BaseEncoding.base16().decode(traveldataInfo);
                ByteBuf byteBuf = Unpooled.buffer(bytes.length);
                byteBuf.writeBytes(bytes);
                m.setBytes(bytes);
                byteBuf.skipBytes(6);
                int dataLength = byteBuf.readUnsignedShort();
                if (dataLength > 0) {
                    int num = dataLength / 234;
                    Byte backWord = byteBuf.readByte();
                    List<AccidentSuspicion2012Item> list1 = new ArrayList<AccidentSuspicion2012Item>();

                    for (int i = 0; i < num; i++) {
                        List<AccidentSuspicion> list2 = new ArrayList<AccidentSuspicion>();
                        AccidentSuspicion2012Item accidentSuspicion2012Item = new AccidentSuspicion2012Item();
                        accidentSuspicion2012Item.setEndTime(ProtoUtil.readTime(byteBuf));
                        accidentSuspicion2012Item.setLic(ProtoUtil.readString(byteBuf, 18));
                        for (int j = 0; j < 100; j++) {
                            AccidentSuspicion accidentSuspicion = new AccidentSuspicion();
                            accidentSuspicion.setSpeed((float) byteBuf.readUnsignedByte());
                            accidentSuspicion.setStatus(byteBuf.readByte());
                            list2.add(accidentSuspicion);
                        }

                        Position position = new Position();
                        position.setLon((int)(byteBuf.readInt() * 0.0001 / 60 * 1000000));
                        position.setLat((int)(byteBuf.readInt() * 0.0001 / 60 * 1000000));
                        position.setHeight((int) byteBuf.readShort());
                        accidentSuspicion2012Item.setPosition(position);

                        accidentSuspicion2012Item.setAsList(list2);
                        list1.add(accidentSuspicion2012Item);
                    }
                    m.setAccidentSuspicion2012Item(list1);
                }
            } else if (m.getCommandType() == (byte) 0x11) {
                String traveldataInfo = ProtoUtil.readU32String(data);
                byte[] bytes = BaseEncoding.base16().decode(traveldataInfo);
                ByteBuf byteBuf = Unpooled.buffer(bytes.length);
                byteBuf.writeBytes(bytes);
                byteBuf.skipBytes(6);
                int dataLength = byteBuf.readUnsignedShort();
                if (dataLength > 0) {

                    int num = dataLength / 50;
                    System.out.println("num=" + num);
                    Byte backWord = byteBuf.readByte();
                    System.out.println("backWord=" + backWord);

                    List<OvertimeDriving2012Item> list1 = new ArrayList<OvertimeDriving2012Item>();

                    for (int i = 0; i < num; i++) {
                        OvertimeDriving2012Item overtimeDriving2012Item = new OvertimeDriving2012Item();
                        overtimeDriving2012Item.setLpn(lpn);//加入车牌号
                        overtimeDriving2012Item.setLic(ProtoUtil.readString(byteBuf, 18));
                        overtimeDriving2012Item.setStartTime(ProtoUtil.readTime(byteBuf));
                        overtimeDriving2012Item.setEndTime(ProtoUtil.readTime(byteBuf));

                        Position startPosition = new Position();
                        startPosition.setLon((int)(byteBuf.readInt() * 0.0001 / 60 * 1000000));
                        startPosition.setLat((int)(byteBuf.readInt() * 0.0001 / 60 * 1000000));
                        startPosition.setHeight((int) byteBuf.readShort());
                        overtimeDriving2012Item.setStartPosition(startPosition);

                        Position lastPosition = new Position();
                        lastPosition.setLon((int)(byteBuf.readInt() * 0.0001 / 60 * 1000000));
                        lastPosition.setLat((int)(byteBuf.readInt() * 0.0001 / 60 * 1000000));
                        lastPosition.setHeight((int)byteBuf.readShort());
                        overtimeDriving2012Item.setLastPosition(lastPosition);

                        list1.add(overtimeDriving2012Item);
                    }
                    m.setOvertimeDriving2012Item(list1);
                }
            }

        } catch (Exception e) {
            m.setRc((byte) 0x00);
            logger.warn("收到主链路，上报车辆行驶记录应答异常", e);
        }
        String fno = "1504:" + lpn + ":" + color;
        DeferredResultUtil.getInstance().setResult(fno, m);
    }

    private void doUpCtrlMsgEmergenctMonitoringAck(String lpn, byte color, ByteBuf data, int dataType) {
        logger.info("-> 收到主链路，车辆应急接入监管平台应答消息，子业务类型 ：0x{}", CodecUtils.shortToHex((short) dataType));
        logger.info("字节数{}，16进制:{}", data.readableBytes(), ByteBufUtil.hexDump(data).replaceAll("\\w{2}(?!$)", "$0 ").toUpperCase());
        Up_1505 m = new Up_1505();
        m.setLpn(lpn);
        m.setCol(color);
        m.setResult(data.readByte());

        String fno = "1505:" + lpn + ":" + color;
        DeferredResultUtil.getInstance().setResult(fno, m);
    }

    private void doDownCtrlMsgMonitorVehicleReq(final DmsContext ctx, Dn_9501 req) {
        Up_1501 res = new Up_1501();

        try {
            Validate.notNull(req.getLpn(), "车牌号不能为空");
            Validate.notNull(req.getCol(), "车辆颜色不能为空");
            Validate.notNull(req.getMonitorTel(), "回拨电话不能为空");
        } catch (Exception e) {
            res.setRc((byte) 0x01);
            res.setErrMsg("请求参数不合法");
            ctx.reply(res);
            return;
        }

        String fno = "1501:" + req.getLpn() + ":" + req.getCol();
        putFuture(fno, ctx, res);



            ProtoMsg out = new ProtoMsg();
            out.msgId = DOWN_CTRL_MSG;
            ByteBuf data = Unpooled.buffer(0);
            out.msgData = data;

            ProtoUtil.writeOctetString(data, req.getLpn(), 21);
            data.writeByte(req.getCol());
            data.writeShort(DOWN_CTRL_MSG_MONITOR_VEHICLE_REQ);
            int startIndex = data.writerIndex();
            data.writeInt(0);//数据长度
            ProtoUtil.writeOctetString(data, req.getMonitorTel(), 20);
            int dataLen = data.writerIndex() - startIndex - 4;
            data.setInt(startIndex, dataLen); //写入实际的数据长度
            ConcurrentMap<Integer, SlaveLink> gov2platMap = PlatformMap.getGov2platMap();


    }

    private void doDownCtrlMsgTakePhotoReq(final DmsContext ctx, Dn_9502 req) {
        Up_1502 res = new Up_1502();


        //判空
        try {
            Validate.notNull(req.getLpn(), "车牌号不能为空");
            Validate.notNull(req.getCol(), "车辆颜色不能为空");
            Validate.notNull(req.getLensId(), "镜头ID不能为空");
            Validate.notNull(req.getSize(), "照片大小不能为空");
        } catch (Exception e) {
            res.setRc((byte) 0x01);
            res.setErrMsg("请求参数不合法");
            ctx.reply(res);
            return;
        }

        String fno = "1502:" + req.getLpn() + ":" + req.getCol();
        putFuture(fno, ctx, res);

        try {
            //父业务
            ProtoMsg out = new ProtoMsg();
            out.msgId = DOWN_CTRL_MSG;
            ByteBuf data = Unpooled.buffer(0);
            out.msgData = data;

            //子业务参数赋值
            ProtoUtil.writeOctetString(data, req.getLpn(), 21); //车牌号
            data.writeByte(req.getCol()); //车牌颜色
            data.writeShort(DOWN_CTRL_MSG_TAKE_PHOTO_REQ); //标识
            int startIndex = data.writerIndex(); //开始指标
            data.writeInt(0);//数据长度
            data.writeByte(req.getLensId());
            data.writeByte(req.getSize());

            int dataLen = data.writerIndex() - startIndex - 4;
            data.setInt(startIndex, dataLen); //写入实际的数据长度


            //根据车辆获取对应的企业平台





        } catch (Exception e) {
            logger.warn("发送车辆拍照请求失败", e);
            DeferredResultUtil.getInstance().setExcepion(fno, e);
        }
    }

    private void doDownCtrlMsgTextInfo(final DmsContext ctx, Dn_9503 req) {
        Up_1503 res = new Up_1503();

        //判空
        try {
            Validate.notNull(req.getLpn(), "车牌号不能为空");
            Validate.notNull(req.getCol(), "车辆颜色不能为空");
            Validate.notNull(req.getMsgSequence(), "消息ID序列不能为空");
            Validate.notNull(req.getMsgPriority(), "报文优先级不能为空");
            Validate.notNull(req.getMsgContent(), "报文内容不能为空");
        } catch (Exception e) {
            res.setRc((byte) 0x01);
            res.setErrMsg("请求参数不合法");
            ctx.reply(res);
            return;
        }

        String fno = "1503:" + req.getLpn() + ":" + req.getCol();
        putFuture(fno, ctx, res);


            //父业务
            ProtoMsg out = new ProtoMsg();
            out.msgId = DOWN_CTRL_MSG;
            ByteBuf data = Unpooled.buffer(0);
            out.msgData = data;

            //子业务参数赋值
            ProtoUtil.writeOctetString(data, req.getLpn(), 21); //车牌号
            data.writeByte(req.getCol()); //车牌颜色
            data.writeShort(DOWN_CTRL_MSG_TEXT_INFO); //标识
            int startIndex = data.writerIndex(); //开始指标
            data.writeInt(0);//数据长度
            data.writeInt(req.getMsgSequence());
            data.writeByte(req.getMsgPriority());
            ProtoUtil.writeU32String(data, req.getMsgContent()); //报文内容

            int dataLen = data.writerIndex() - startIndex - 4;
            data.setInt(startIndex, dataLen); //写入实际的数据长度
            ConcurrentMap<Integer, SlaveLink> gov2platMap = PlatformMap.getGov2platMap();//获取从链路Map

            //根据车辆获取对应的企业平台




    }

    private void doDownCtrlMsgTakeTravelReq(final DmsContext ctx, Dn_9504 req) {
        Up_1504 res = new Up_1504();

        //判空
        try {
            Validate.notNull(req.getLpn(), "车牌号不能为空");
            Validate.notNull(req.getCol(), "车辆颜色不能为空");
            Validate.notNull(req.getCommandType(), "命令字不能为空");
        } catch (Exception e) {
            res.setRc((byte) 0x01);
            res.setErrMsg("请求参数不合法");
            ctx.reply(res);
            return;
        }

        String fno = "1504:" + req.getLpn() + ":" + req.getCol();
        putFuture(fno, ctx, res);

        try {
            //父业务
            ProtoMsg out = new ProtoMsg();
            out.msgId = DOWN_CTRL_MSG;
            ByteBuf data = Unpooled.buffer(0);
            out.msgData = data;

            //子业务参数赋值
            ProtoUtil.writeOctetString(data, req.getLpn(), 21); //车牌号
            data.writeByte(req.getCol()); //车牌颜色
            data.writeShort(DOWN_CTRL_MSG_TAKE_TRAVEL_REQ); //标识
            int startIndex = data.writerIndex(); //开始指标
            data.writeInt(0);//数据长度
            data.writeByte(req.getCommandType());

            int dataLen = data.writerIndex() - startIndex - 4;
            data.setInt(startIndex, dataLen); //写入实际的数据长度
            ConcurrentMap<Integer, SlaveLink> gov2platMap = PlatformMap.getGov2platMap();//获取从链路Map


        } catch (Exception e) {
            logger.warn("发送车辆行驶记录采集请求失败", e);
            DeferredResultUtil.getInstance().setExcepion(fno, e);
        }

    }

    private void doUpCtrlMsgEmergencyMonitoringReq(final DmsContext ctx, Dn_9505 req) {
        Up_1505 res = new Up_1505();

        //判空
        try {
            Validate.notNull(req.getLpn(), "车牌号不能为空");
            Validate.notNull(req.getCol(), "车辆颜色不能为空");
            Validate.notNull(req.getAuthCode(), "鉴权码不能为空");
            Validate.notNull(req.getAccessPointName(), "拨号点名称不能为空");
            Validate.notNull(req.getUserName(), "拨号用户名不能为空");
            Validate.notNull(req.getPassword(), "拨号密码不能为空");
            Validate.notNull(req.getServerIp(), "服务器地址不能为空");
            Validate.notNull(req.getTcpPort(), "服务器tcp端口不能为空");
            Validate.notNull(req.getUdpPort(), "服务器udp端口不能为空");
            Validate.notNull(req.getEndTime(), "结束时间不能为空");
        } catch (Exception e) {
            res.setRc((byte) 0x01);
            res.setErrMsg("请求参数不合法");
            ctx.reply(res);
            return;
        }

        String fno = "1505:" + req.getLpn() + ":" + req.getCol();
        putFuture(fno, ctx, res);

        try {
            //父业务
            ProtoMsg out = new ProtoMsg();
            out.msgId = DOWN_CTRL_MSG;
            ByteBuf data = Unpooled.buffer(0);
            out.msgData = data;

            //子业务参数赋值
            ProtoUtil.writeOctetString(data, req.getLpn(), 21); //车牌号
            data.writeByte(req.getCol()); //车牌颜色
            data.writeShort(DOWN_CTRL_MSG_EMERGENCY_MONITORING_REQ); //标识
            int startIndex = data.writerIndex(); //开始指标
            data.writeInt(0);//数据长度
            ProtoUtil.writeBytes(data, req.getAuthCode(), 10);
            ProtoUtil.writeOctetString(data, req.getAccessPointName(), 20); //拨号点名称
            ProtoUtil.writeOctetString(data, req.getUserName(), 49);
            ProtoUtil.writeOctetString(data, req.getPassword(), 22);
            ProtoUtil.writeOctetString(data, req.getServerIp(), 32);
            data.writeShort(req.getTcpPort());
            data.writeShort(req.getUdpPort());
            data.writeLong(req.getEndTime());

            int dataLen = data.writerIndex() - startIndex - 4;
            data.setInt(startIndex, dataLen); //写入实际的数据长度
            ConcurrentMap<Integer, SlaveLink> gov2platMap = PlatformMap.getGov2platMap();//获取从链路Map



        } catch (Exception e) {
            logger.warn("发送车辆应急接入监管平台请求请求失败", e);
            DeferredResultUtil.getInstance().setExcepion(fno, e);
        }

    }

}
